﻿/*
<File>
	<Copyright>Copyright © 2007, Outcoder. All rights reserved.</Copyright>
	<License see="//License.txt"/>
	<Owner Name="Daniel Vaughan" Email="danielvaughan@outcoder.com"/>
	<CreationDate>2007/11/27 12:44</CreationDate>
	<LastSubmissionDate>$Date: $</LastSubmissionDate>
	<Version>$Revision: $</Version>
</File>
*/

using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;

using Microsoft.Practices.EnterpriseLibrary.Logging;

namespace Outcoder.Logging.LogStrategies
{
	/// <summary>
	/// A log strategy for the Microsoft Patterns and Practices
	/// Enterprise Library Logging Application Block.
	/// It's fairly rudimentary, and converts
	/// <see cref="LogLevel"/>s into Categories,
	/// Priorities, and Severities; perhaps
	/// oversimplifying the Enterprise Library Logging.
	/// </summary>
	class EnterpriseLibraryStrategy : ILogStrategy
	{
		static readonly LogLevel[] logLevels = new []
				{
					LogLevel.All, LogLevel.Debug, LogLevel.Info, 
					LogLevel.Warn, LogLevel.Error, LogLevel.Fatal
				};

		public LogLevel GetLogLevel(IClientInfo clientInfo)
		{
			foreach (LogLevel level in logLevels)
			{
				if (ShouldLog(level))
				{
					return level;
				}
			}
			
			return LogLevel.None;
		}

		static bool ShouldLog(LogLevel level)
		{
			var logEntry = new LogEntry();
			logEntry.Priority = (int)level;
			return Logger.ShouldLog(logEntry);
		}

		public void Write(IClientLogEntry logEntry)
		{
			LogEntry elEntry = new LogEntry();
			elEntry.MachineName = logEntry.MachineName;
			elEntry.ManagedThreadName = logEntry.ManagedThreadId.ToString();
			elEntry.Message = logEntry.Message;
			elEntry.Priority = (int)logEntry.LogLevel;
			elEntry.Severity = Convert(logEntry.LogLevel);
			elEntry.TimeStamp = logEntry.OccuredAt;
//			elEntry.ManagedThreadName
//			elEntry.ProcessName
			elEntry.Categories.Add("General");
			elEntry.Categories.Add(logEntry.LogLevel.ToString("G"));

			if (logEntry.ExceptionMemento != null)
			{
				elEntry.AddErrorMessage(logEntry.ExceptionMemento.ToString());
			}

			var properties = logEntry.Properties;
			if (properties == null)
			{
				properties = new Dictionary<string, object>();
			}

			if (!properties.Keys.Contains("CodeLocation"))
			{
				properties.Add("CodeLocation", logEntry.CodeLocation);
			}

			WriteThreadSafe(elEntry, logEntry.Properties);
		}

		public void Write(IServerLogEntry logEntry)
		{
			LogEntry elEntry = new LogEntry();
			elEntry.MachineName = logEntry.MachineName;
			elEntry.ManagedThreadName = logEntry.ManagedThreadId.ToString();
			elEntry.Message = logEntry.Message;
			elEntry.Priority = (int)logEntry.LogLevel;
			elEntry.Severity = Convert(logEntry.LogLevel);
			elEntry.TimeStamp = logEntry.OccuredAt;
			elEntry.AppDomainName = logEntry.AppDomain;
/* TODO: Populate these extra bits. */
//			elEntry.ManagedThreadName
//			elEntry.ProcessName = 
			elEntry.Categories.Add("General");
			elEntry.Categories.Add(logEntry.LogLevel.ToString("G"));

			if (logEntry.Exception != null)
			{
				string errorMessage = string.Format("{0}\n{1}", 
					logEntry.Exception, logEntry.Exception.StackTrace);
				elEntry.AddErrorMessage(errorMessage);
			}

			var properties = logEntry.Properties;
			if (properties == null)
			{
				properties = new Dictionary<string, object>();
			}

			if (!properties.Keys.Contains("CodeLocation"))
			{
				properties.Add("CodeLocation", logEntry.CodeLocation);
			}

			WriteThreadSafe(elEntry, properties);
		}

		/// <summary> 
		/// All Write log calls are done asynchronously because the Outcoder.Logging.Log 
		/// uses the AppPool to dispatch calls to this method. Therefore we need to ensure 
		/// that the call to Log is threadsafe. That is, if an appender such as FileAppender is used, 
		/// then we need to ensure it is called from no more than one thread at a time. 
		/// </summary>
		[MethodImpl(MethodImplOptions.Synchronized)]
		static void WriteThreadSafe(LogEntry logEntry, IDictionary<string, object> properties)
		{
			Logger.Write(logEntry, properties);
		}

		static TraceEventType Convert(LogLevel level)
		{
			switch (level)
			{
				case LogLevel.All:
				case LogLevel.Debug:
					return TraceEventType.Verbose;
				case LogLevel.Info:
					return TraceEventType.Information;
				case LogLevel.Warn:
					return TraceEventType.Warning;
				case LogLevel.Error:
					return TraceEventType.Error;
				case LogLevel.Fatal:
					return TraceEventType.Critical;
				default:
					return TraceEventType.Information;
			}
		}
	}
}
